﻿using System.Collections.Generic;
using System.Linq;
using DependencyAnalysis.Algorithms.SubstringDetection;
using DependencyAnalysis.DataModel;

namespace DependencyAnalysis.Internal
{
    public static class DontRepeatYourselfPrinciple
    {
        public class InstructionsByMembers
        {
            public IList<MemberDescriptor> Members;
            public IList<char[]> InstructionSequences;
        }

        public static IEnumerable<CodeDuplicate> FindDuplicatesInsideMembers(int minimalLength, InstructionsByMembers memberInstructions)
        {
            var duplicatesInside = new List<CodeDuplicate>();
            for (int index = 0; index < memberInstructions.Members.Count; index++)
            {
                var member = memberInstructions.Members[index];
                var instructions = memberInstructions.InstructionSequences[index];
                if (instructions.Length < 2 * minimalLength) continue;
                var suffixTree = new SuffixTree(memberInstructions.InstructionSequences[index]);
                var substring = suffixTree.FindLongestRepeatition();
                if (substring == null || substring.Length < minimalLength) continue;
                var members = new MemberDescriptor[substring.BeginIndices.Length];
                var indices = new List<int>();
                for (int i = 0; i < substring.BeginIndices.Length; i++)
                {
                    members[i] = member;
                    indices.Add(substring.BeginIndices[i]);
                }
                var duplicate = new CodeDuplicate
                    {
                        NumberOfInstructions = substring.Length,
                        Members = members,
                        StartIndices = indices.ToArray()
                    };
                duplicatesInside.Add(duplicate);
            }
            return duplicatesInside;
        }

        public static List<CodeDuplicate> FindDuplicatesBetweenMembers(int minimalLength, InstructionsByMembers memberInstructions)
        {
            var duplicates = new List<CodeDuplicate>();
            var generalizedSuffixTree = new GeneralizedSuffixTree(memberInstructions.InstructionSequences.ToArray());
            var commonSubstrings = generalizedSuffixTree.FindCommonSubstrings(minimalLength);
            foreach (var substring in commonSubstrings)
            {
                var members = new List<MemberDescriptor>();
                var indices = new List<int>();
                for (int i = 0; i < substring.TextIndices.Length; i++)
                {
                    members.Add(memberInstructions.Members[substring.TextIndices[i]]);
                    indices.Add(substring.BeginIndices[i]);
                }
                var duplicate = new CodeDuplicate
                    {
                        NumberOfInstructions = substring.Length,
                        Members = members.ToArray(),
                        StartIndices = indices.ToArray()
                    };
                duplicates.Add(duplicate);
            }
            return duplicates;
        }

        public static InstructionsByMembers SelectRelevantMembers(int minimalLength, IEnumerable<MemberDescriptor> memberHash, InstructionCache cache)
        {
            var relevantMembers = new List<MemberDescriptor>();
            var instructionSequences = new List<char[]>();
            foreach (MemberDescriptor member in memberHash)
            {
                if (member.IsGenerated || member.MemberType != MemberType.Method || member.Name == ".cctor") continue;
                var instructions = cache.GetInstructions(member);
                if (instructions.Count < minimalLength) continue;
                relevantMembers.Add(member);
                var instructionString = new char[instructions.Count];
                for (int index = 0; index < instructions.Count; index++)
                {
                    var instruction = instructions[index];
                    instructionString[index] = (char)(int)instruction.OpCode;
                }
                instructionSequences.Add(instructionString);
            }
            return new InstructionsByMembers { Members = relevantMembers, InstructionSequences = instructionSequences };
        }
    }
}
